/*******************************************************************************
 * Copyright (c) 2008-2010 Cedric Vidal
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Cedric Vidal - original idea, initial contribution and API
 *******************************************************************************/

package org.trans4j;

import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

/**
 * @author <a href="mailto:c.vidal@proxiad.com">Cedric Vidal, ProxiAD Group</a>
 * 
 */
public class DynamicPolymorphicDispatchTest {

	@Test
	public void polymorphicDispatch() {
		LeftParent b = new LeftParent();
		b.setLeftName("b");
		b.getLeftChilds().add(leftChildType1("l1"));
		b.getLeftChilds().add(leftChildType2("l2"));

		RightParent a = TestMapper.INSTANCE.parent(b);
		assertEquals(2, a.getRightChilds().size());
		assertThat(a.getRightChilds().get(0), instanceOf(RightChildType1.class));
		assertThat(a.getRightChilds().get(1), instanceOf(RightChildType2.class));
	}

	private LeftChildType1 leftChildType1(String name) {
		LeftChildType1 leftChild = new LeftChildType1();
		leftChild.setLeftName(name);
		return leftChild;
	}

	private LeftChildType2 leftChildType2(String name) {
		LeftChildType2 leftChild = new LeftChildType2();
		leftChild.setLeftName(name);
		return leftChild;
	}

	static class TestMapper {
		public static final TestMapper INSTANCE = Trans4j
				.mapper(TestMapper.class);

		public RightParent parent(LeftParent b) {
			RightParent a = parent(b.getLeftName());
			for (LeftChild c : b.getLeftChilds()) {
				a.getRightChilds().add(child(c));
			}
			a.setChild1(child(b.getLeftChilds().get(0)));
			a.setChild2(child(b.getLeftChilds().get(0)));
			return a;
		}

		@CreateRule
		protected RightParent parent(String leftName) {
			RightParent a = new RightParent();
			a.setRightName(leftName);
			return a;
		}

		public RightChild child(LeftChild b) {
			RightChild a = new RightChild();
			a.setRightName(b.getLeftName());
			return a;
		}

		public RightChildType1 child(LeftChildType1 b) {
			RightChildType1 a = new RightChildType1();
			a.setRightName(b.getLeftName());
			return a;
		}

		public RightChildType2 child(LeftChildType2 b) {
			RightChildType2 a = new RightChildType2();
			a.setRightName(b.getLeftName());
			return a;
		}

	}

	static class RightParent {
		private String rightName;

		public String getRightName() {
			return rightName;
		}

		public void setRightName(String name) {
			this.rightName = name;
		}

		private List<RightChild> rightChilds = new ArrayList<RightChild>();

		public List<RightChild> getRightChilds() {
			return rightChilds;
		}

		public void setRightChilds(List<RightChild> rightChilds) {
			this.rightChilds = rightChilds;
		}

		private RightChild child1;

		public RightChild getChild1() {
			return child1;
		}

		public void setChild1(RightChild firstChild) {
			this.child1 = firstChild;
		}

		private RightChild child2;

		public RightChild getChild2() {
			return child2;
		}

		public void setChild2(RightChild child2) {
			this.child2 = child2;
		}
	}

	static class RightChild {
		private String rightName;

		public String getRightName() {
			return rightName;
		}

		public void setRightName(String name) {
			this.rightName = name;
		}
	}

	static class RightChildType1 extends RightChild {
		private String rightName;

		public String getRightName() {
			return rightName;
		}

		public void setRightName(String name) {
			this.rightName = name;
		}
	}

	static class RightChildType2 extends RightChild {
		private String rightName;

		public String getRightName() {
			return rightName;
		}

		public void setRightName(String name) {
			this.rightName = name;
		}
	}

	static class LeftParent {
		private String leftName;

		public String getLeftName() {
			return leftName;
		}

		public void setLeftName(String name) {
			this.leftName = name;
		}

		private List<LeftChild> leftChilds = new ArrayList<LeftChild>();

		public List<LeftChild> getLeftChilds() {
			return leftChilds;
		}

		public void setLeftChilds(List<LeftChild> leftChilds) {
			this.leftChilds = leftChilds;
		}
	}

	static class LeftChild {
		private String leftName;

		public String getLeftName() {
			return leftName;
		}

		public void setLeftName(String name) {
			this.leftName = name;
		}
	}

	static class LeftChildType1 extends LeftChild {
		private String leftName;

		public String getLeftName() {
			return leftName;
		}

		public void setLeftName(String name) {
			this.leftName = name;
		}
	}

	static class LeftChildType2 extends LeftChild {
		private String leftName;

		public String getLeftName() {
			return leftName;
		}

		public void setLeftName(String name) {
			this.leftName = name;
		}
	}

}
